package cn.edu.buaa.purain.rest;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.servlet.http.HttpServlet;


@SuppressWarnings("unchecked")
public class AnnotationResolver {
	
	private PurainServletFactory servletFactory = null;
	
	public PurainServletFactory getServletFactory() {
		return servletFactory;
	}
	public void setServletFactory(PurainServletFactory servletFactory) {
		this.servletFactory = servletFactory;
	}
	
	public AnnotationResolver(PurainServletFactory servletFactory) {
		this.servletFactory = servletFactory;
	}

	@SuppressWarnings("rawtypes")
	public void readServletClasses(String pack) throws InstantiationException, IllegalAccessException {
		Set<Class<?>> classes = getClasses(pack);
		
		for (Class c : classes) {
			if (c.getSuperclass() != HttpServlet.class) {
				continue;
			}
			
			if (! c.isAnnotationPresent(PurainRequestMapping.class)) {
				continue;
			}
			
			PurainRequestMapping annotation = (PurainRequestMapping)c.getAnnotation(PurainRequestMapping.class);
			String path = annotation.path();
			String[] httpPara = annotation.httpPara();
			
			String key = PurainUrlHelper.httpPara2String(httpPara);
			
			if (PurainUrlHelper.isRegexPath(path)) {
				//url contain '*'
				
				path = PurainUrlHelper.urlPath2RegexPath(path);
				servletFactory.getRegexMap().put(key + path, (HttpServlet)c.newInstance());
			}else {
				servletFactory.getRealPathMap().put(key + path, (HttpServlet)c.newInstance());
			}
		}
	}
	
	public Set<Class<?>> getClasses(String pkg) {
		Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
		
		boolean recursive = true;
		String packageName = pkg.replace('.', '/');
		
		Enumeration<URL> dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources(packageName);
			
			while (dirs.hasMoreElements()) {
				URL url = dirs.nextElement();
				
				//the protocol means the file type, like file or jar
				String protocol = url.getProtocol();
				if ("file".equals(protocol)) {
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					AddClassFromPackage(packageName, filePath, recursive, classes);
					
				}
			}

		}catch (Exception ex) {
			ex.printStackTrace();
		}
		
		
		return classes;
	}

	private void addClass(Set<Class<?>> classes, String packageName, String classFileName) {
		String className = classFileName.substring(0, classFileName.length()-6);
		
		try {	
			packageName = packageName.replace('/', '.');
			classes.add(Class.forName(packageName + "." + className));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	private void AddClassFromPackage(String packageName,
			String filePath, final boolean recursive, Set<Class<?>> classes) {
		
		File dir = new File(filePath);
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
			
		File[] dirFiles = dir.listFiles(new FileFilter() {

			@Override
			public boolean accept(File file) {
				boolean b = recursive && file.isDirectory();
				b = b || file.getName().endsWith(".class");
				return b;
			}
			
		});
		
		for (File file : dirFiles) {
			if (file.isDirectory()) {
				AddClassFromPackage(packageName + "/" + file.getName(), 
						file.getAbsolutePath(), recursive, classes);
			}else {
				addClass(classes, packageName, file.getName());
			}
		}
		
	}
}
